The present invention relates generally to the reuse of software code within the area of object-oriented programming languages, and more specifically to a system constructed in accordance with a programming language where software reusability is achieved by the use of reusable software units (or connectons), defined modularly and hierarchically, and by the removal of all absolute addresses from connectons.
Software reusability is an attribute of software that facilitates its incorporation into new application programs. Software reusability has become a timely topic because of economic reasons, i.e., projects that reuse code are cheaper and take less time to complete. During the early days of computer programming, the most successful examples of software reuse were subroutine libraries. More recently, Software Design Patterns are considered to be a promising reuse technology.
I. Object Oriented Programming
A programming language is object oriented if it supports objects as a language feature, and objects belong to classes that can be incrementally modified through inheritance (i.e., Simula, Smalltalk, C++, Eiffel and Ada). The essence of object oriented programming is the hiding or encapsulation of the “inner” state of entities and the specification of interactive properties of entities by an interface of operations (the events in which they may participate).
Object-oriented languages encapsulate data as well as sequences of actions, providing a stronger encapsulation mechanism than procedures, and consequently, a more powerful modeling tool. The state of objects is to serve as a repository of data (current system state). Object-oriented programming is a modeling paradigm that represents objects of the real world by collections of interacting objects of a programming system.
Objects in programming languages are collections of operations, which share a state. The operations determine the messages (calls) to which the object can respond, while the shared state is hidden from the outside world and accessible only by object operation. Variables representing the internal state of an object are called instance variables, and object operations are called methods. The collection of methods of an object determines its interface and behavior.
Classes serve as templates from which objects may be created. Different flavors exist and, for example, in Smalltalk, classes are also objects, and exist as such even if no objects are created. Inheritance is a mechanism for sharing code and behavior, allowing the reuse of behavior of a class in the definition of new classes. Subclasses of a class inherit the operations of their parent class and may add new operations and new instance variables. Polymorphism, the ability to invoke different code to respond to the same message name, is also considered as an important characteristic of object-oriented languages.
However, even in the more regimented atmosphere of object oriented code (e.g., Smalltalk), there is still no accepted methodology to create repositories of software, which would facilitate software reuse. For that matter, other technological fields do not suffer from the software reuse problem. For example, repositories of hardware for future use are known. For that matter, the problem with objects, as distinguished from integrated circuits (ICs) is that 1) contemporary object components do not behave independently of each other; and 2) objects cannot be built in a hierarchical and modular form. The reason for the first problem is that object technology does not provide a transparent mechanism for communication among objects, as compared to ICs, which are independent from each other. The reason for the second problem is that in traditional object oriented languages, one cannot create new objects by composition of other objects, severely limiting the ability to build large hierarchical systems.
II. Software Reuse
Perhaps one of the most successful paradigms for software reuse is the model-view-controller (MVC) concept introduced by Smalltalk language, that has influenced the JavaBeans component model, see A. DeSoto. Using the Beans Development Kit 1.0. JavaSoft, 1997. The dependence mechanism provides some support for reuse. An object can have a collection of dependents, and when an object changes its state, it can send itself an update message; this message then sends an update message to all the dependents. Thus, an object can be built without any knowledge of its peers. The dependence mechanism permits the reuse of objects in different contexts. Also, because the dependent list can be changed in run-time, this method provides support for managing adaptive behavior.
The dependence mechanism has, however, several inconveniences. First, all the dependents must have an update method, and second, when an update message is issued, all the dependents receive the message. The message broadcast can become very inefficient, and because the update message is so general, all the parameters must be sent as arguments, making it very difficult to introduce new types of objects.
Adapters provide a mechanism for code reuse, and were first implemented in VisualWorks, see E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. Adapters attempt to solve the problem of connecting objects with different interfaces. In this framework, when objects with incompatible interfaces need to communicate, an adapter is created to transform message calls of one object in message calls the second object can understand.
Pipes, despite their simplicity, were one of the first constructs to provide software reuse. Pipes used in the UNIX operating systems provide an interesting mechanism for software reuse. Programs in the operating system can behave independently sending their outputs through a pipe to another program. Pipes have severe limitations, however. They can pass only unstructured data like characters, and they provide only a one-direction mechanism.
Producing software “ICs” has long been advised as a solution to software reuse, B. J. Cox. Object Oriented Programming: An Evolutionary Approach. Addison-Wesley, 1987. Very interesting applications of this concept can be found in, S. Koshafian and R. Abnous. Object Orientation: Concepts, Languages, Databases and User Interfaces. John Wiley, 1989, where a hardware application is described, and recently in, B. P. Zeigler. Objects and Systems: Principled Design with Implementation in C++ and Java. Springer Verlag, 1997. The construction of hierarchical and modular objects is known to be achievable by replacing object absolute addresses by parameters that are only assigned at run time when the object is created. This permits the use of the same object in different contexts (i.e., applications). While this approach is beneficial, it still includes the inherent drawback that the name of the message sent to the outside of the object is still hard-coded.
Patterns were thought to be a promising solution to software reuse, and were created to explore the limits of object oriented programming, see E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. Patterns categorize and exemplify how a set of classes may be used to construct a certain system. Patterns constitute a case by case reuse, but are limited. Patterns do not support the construction of independent units of software, but rather the programmer must incorporate the pattern design in the implementation. A good overview of software reuse techniques can be found in, J. Sametinger. Software Engineering with Reusable Components. Springer Verlag, 1997, and C. Szyperski. Component Software: Beyond Object-Oriented Programming. Addison-Wesley, 1998.
III. Why Objects Fail
Objects have been recognized as the solution to software low productivity. Object reuse by means of inheritance or by the use of Abstract Data Types seemed to be promising. However, those skilled in the arts soon realized that these promises were not fulfilled by the new technology. Reuse of low level objects has shown to be much easier than reuse of an overall design.
Furthermore, the object solution seems not to scale. Simple objects like lists and dictionaries can easily be reused but complex applications cannot. For example, reusing a graphical editor as a whole is more complex than reusing the constituting classes when they are viewed as independent units. The problem becomes more evident as the size and complexity of the application begins to grow. This difficulty has been assigned to the lack of comments in the code, and also to the low commitment to reuse by software engineers. Although some part of this may be true, we believe that the problem only becomes marginally smaller by using informal techniques. We are convinced that the object methodology is responsible for the crises, and consequently, a paradigm shift towards more powerful concepts is needed.
Difficulty also arises from the fact that higher level programming languages, from their very beginnings, are based on the limitations of the machines in which they were designated to operate, i.e., they try to make efficient use of hardware while improving human readability. The generalized use of pointers and subroutine calls is symptomatic of a machine-oriented design. Although objects (in object oriented programming languages) have introduced the concept and use of “state”, object messages are still not very different from routine or procedure calls and use.
Developed in a different science, “General Systems Theory”, M. D. Mesarovic and Y. Takahara. General Systems Theory: A Mathematical Foundation. Academic Press, 1975 and B. P. Zeigler. Theory of Modeling and Simulation. John Wiley, 1976, the concept of “system”, appears to provide a better abstraction than objects by providing modular and hierarchical description and offering the concept of polymorphism. Systems, however, are still unable to provide a basis for software reusability. The implementation of system concepts is contrary to the design of “quick and dirty” programming languages, required during the time that computers were quite slow. In addition, due to the contrast between the timed, asynchronous and pull nature of systems, and the non-timed, synchronous and push nature of most programming languages, adapting systems concepts to programming languages is a substantial problem.
IV. Object Axioms
In almost every language a routine, a function, or a message call has one of the following formats:[variable :=] object message [arguments].  (1) [variable :=] self message [arguments].  (2) Brackets mean that the term is optional. Without loss of generality, we use the Smalltalk convention for message calls. In non-object oriented languages, what is termed as an “object” is usually sent as an argument to a routine, that have been renamed by message in the “object paradigm” (of object oriented programming languages).
Statement 2 poses no problem for reuse for it just assumes that an object knows its own details. However, Statement 1, in spite of its simplicity and its apparent inoffensiveness, retains all the four features that destroy any serious attempt to software reuse, no matter how much effort is employed. These 4 features (the group of four, or G4 for short) kill any chances of producing real software ICs. This G4 assumption can be described by the following four axioms:                I) Existence of the receiver. Statement 1 assumes that the receiver object does exist.        II) Uniqueness of the receiver. Statement 1 assumes that the message is sent to just one object.        III) Knowledge of the receiver. Statement 1 assumes that the object address is known.        IV) Knowledge of the message. Statement 1 assumes that the method invoked in the receiver object is known.        
This is too much for just one simple assignment, and these assumptions have destroyed any chance of conventional object reuse. Hence, these underlying 4 axioms prove to be deadly to software reuse in any of today's paradigms. While several software paradigms have reduced some of these constraints, no conventional systems are known to exist that have reduced them all. Although these axioms are evident, their limitations to software reuse seem never to have been fully described and exploited.
More particularly, Axiom I states that if an object must communicate with another object, this other object must exist. This assumption, which seems to be a universal rule, is fallacious. A software entity must work without any assumption about its neighbors, even about their existence, if it is to be able to operate independently. Axiom II states that objects communicate on a one-to-one basis. The same comments set forth with respect to axiom I can be applied here.
Axiom III assumes that the object absolute address is known. Absolute address is another factor that makes reuse very difficult. Objects that have addresses hard-coded by no means can be used in different contexts. To reuse objects that satisfy this axiom, it is necessary to replace absolute addresses by new ones, and then recompile the system again. Because addresses are scattered through the code, these changes are virtually impossible in complex systems.
Axiom IV is where machine oriented language design is more visible and where it makes more damage. Deciding the name of the message to send to an object is always fixed in programming languages.
The Smalltalk dependence mechanism was probably the first successful attempt to remove axioms I and II. The reader should note that the dependence mechanism might be described as a relationship between objects where one object can receive information about changes made in another object. In this framework update messages are sent to a list of dependents, and this list can have zero or more elements.
Axioms III and IV have been partially removed by the use of adapters. Instead of sending messages directly to an object, messages are sent to an intermediate object known as the adapter that converts one object interface to another. Facade systems based on graphical systems give the false impression that axioms III and IV have been removed. However, the underlying mechanism is so complex that the best that can be expected is that the components existing in the library are enough to build an application, otherwise lots of difficult tricks must be mastered to complete the programming task, see Extend User's Manual. Imagine That Inc., 1997.
Statements 1 and 2 also assume an underlying synchronous design. In most of the programming languages the execution control waits until the call of statement 1 terminates before executing the next statement. In asynchronous programming languages, usually running multiple threads of control, the execution of statements 1 does not need to terminate before the control of execution is given to the next statement. Some programming languages provide a mixture of these 2 characteristics, and the programmer can choose what calls are synchronous and what call are asynchronous. However, synchronous and asynchronous characteristics are orthogonal to the problem of software reuse.